/******************************************************************************\
    ContestantDataBase.h discribes a database of contestants.
    It's used with acm server. See main.cpp for details.
    Copyright (C) 2008  Victor Vinogradov (fly3333@gmail.com)

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
\******************************************************************************/

#pragma once 

#include ".\BaseDataBase.hpp"
#include <mbstring.h>
#include <mbctype.h>
#include <locale.h>

namespace nsContestantDataBase
{
	const unsigned long STATUS_CONTESTANTS_BROWSING           = 0x00000001ul;
	const unsigned long STATUS_CONTESTANT_EDITING             = 0x00000006ul;
		const unsigned long STATUS_CONTESTANT_EDITING_DENIED      = 0x00000000ul;
		const unsigned long STATUS_CONTESTANT_EDITING_HIMSELF     = 0x00000002ul;
		const unsigned long STATUS_CONTESTANT_EDITING_BASIC       = 0x00000004ul;
		const unsigned long STATUS_CONTESTANT_EDITING_FULL        = 0x00000006ul;
	const unsigned long STATUS_CLIENTS_BROWSING               = 0x00000008ul;
	const unsigned long STATUS_PLUGINS_BROWSING               = 0x00000010ul;
	const unsigned long STATUS_PLUGIN_EDITING                 = 0x00000020ul;
	const unsigned long STATUS_PROXY_EDITING                  = 0x00000040ul;
	const unsigned long STATUS_CONTESTS_BROWSING              = 0x00000080ul;
	const unsigned long STATUS_CONTEST_EDITING                = 0x00000100ul;
	const unsigned long STATUS_CONTEST_STARTING               = 0x00000200ul;
	const unsigned long STATUS_BOT                            = 0x00000400ul;
	const unsigned long STATUS_MANAGE                         = 0x00000800ul;
	const unsigned long STATUS_CONTEST_WATCHING               = 0x00001000ul;

	const unsigned long STATUS_AUTO = STATUS_BOT;
	const unsigned long STATUS_GUEST = STATUS_CONTESTANTS_BROWSING | STATUS_CONTESTS_BROWSING;
	const unsigned long STATUS_ADMIN = STATUS_MANAGE | STATUS_CONTEST_STARTING | STATUS_CONTEST_EDITING | STATUS_CONTESTS_BROWSING | STATUS_PROXY_EDITING | STATUS_PLUGIN_EDITING | STATUS_PLUGINS_BROWSING | STATUS_CLIENTS_BROWSING | STATUS_CONTESTANT_EDITING_FULL | STATUS_CONTESTANT_EDITING | STATUS_CONTESTANTS_BROWSING | STATUS_CONTEST_WATCHING;

	const size_t MAX_NAME_LENGTH = 255;
	const size_t MAX_LOGIN_LENGTH = 63;
	const size_t MAX_PASSWORD_LENGTH = 63;
	const size_t MAX_COMMENT_LENGTH = 255;

	struct sContestantData0
	{
		unsigned long Key;
		char Name[ MAX_NAME_LENGTH + 1 ];
		char Login[ MAX_LOGIN_LENGTH + 1 ];
		char Password[ MAX_PASSWORD_LENGTH + 1 ];
		unsigned long Status;
		char Comment[ MAX_COMMENT_LENGTH + 1 ];
	};

	struct sContestantData1
	{
		unsigned long Key;
		char Name[ MAX_NAME_LENGTH + 1 ];
		char Login[ MAX_LOGIN_LENGTH + 1 ];
		char Password[ MAX_PASSWORD_LENGTH + 1 ];
		unsigned long Status;
		char Comment[ MAX_COMMENT_LENGTH + 1 ];
	};

	typedef sContestantData1 sContestantData;

	void ConversionFunction0( void *new_data, const void *prev_data );

	const sConversionTable ConversionTable[] =
	{
		{ sizeof( sContestantData0 ), sizeof( sContestantData1 ), &ConversionFunction0 }
	};

	struct NameCompare : public std::binary_function< cDataPointer< sContestantData >, cDataPointer< sContestantData >, bool >
	{
		bool operator()( const cDataPointer< sContestantData > &left, const cDataPointer< sContestantData > &right ) const
		{			
			int compare_result = _stricmp( left->Name, right->Name );
			return compare_result < 0 ||
				    compare_result == 0 && left->Key < right->Key;
		}
	};

	struct LoginCompare : public std::binary_function< cDataPointer< sContestantData >, cDataPointer< sContestantData >, bool >
	{
		bool operator()( const cDataPointer< sContestantData > &left, const cDataPointer< sContestantData > &right ) const 
		{
			return _stricmp( left->Login, right->Login ) < 0;
		}
	};

	typedef cBaseDataBase< sContestantData >::KeyIterator KeyIterator;
	typedef cPublicIterator< sContestantData, NameCompare > NameIterator;
	typedef cPublicIterator< sContestantData, LoginCompare > LoginIterator;

	class cWardContestantDataBase : public cBaseDataBase< sContestantData >
	{
	private:
		std::set< cDataPointer< sContestantData >, NameCompare > NameIndex;
		std::set< cDataPointer< sContestantData >, LoginCompare > LoginIndex;
	protected:
		virtual bool Indexed( const cDataPointer< sContestantData > &data_pointer )
		{
			return /*NameIndex.find( data_pointer ) != NameIndex.end() ||*/
				    LoginIndex.find( data_pointer ) != LoginIndex.end();
		}
		virtual void InsertIndex( const cDataPointer< sContestantData > &data_pointer )
		{
			NameIndex.insert( data_pointer );
			LoginIndex.insert( data_pointer );
		}
		virtual void UpdateIndex( const cDataPointer< sContestantData > &data_pointer, unsigned long index )
		{
			NameIndex.find( data_pointer )->Index = index;
			LoginIndex.find( data_pointer )->Index = index;
		}
		virtual void DeleteIndex( const cDataPointer< sContestantData > &data_pointer )
		{
			NameIndex.erase( data_pointer );
			LoginIndex.erase( data_pointer );
		}
		virtual void ClearIndex()
		{
			NameIndex.clear();
			LoginIndex.clear();
		}
	public:
		cWardContestantDataBase( /*bool auto_update*/ )
			: cBaseDataBase( /*auto_update*/ )
		{
		}
		NameIterator BeginByName() const
		{
			return NameIterator( NameIndex.begin() );
		}
		NameIterator FindByName( const char name[] ) const
		{
			if( strlen( name ) > MAX_NAME_LENGTH )
				return EndByName();
			sContestantData contestant_data;
			strcpy( contestant_data.Name, name );
			return NameIterator( NameIndex.find( cDataPointer< sContestantData >( &contestant_data ) ) );
		}
		NameIterator EndByName() const
		{
			return NameIterator( NameIndex.end() );
		}
		LoginIterator BeginByLogin() const
		{
			return LoginIterator( LoginIndex.begin() );
		}
		LoginIterator FindByLogin( const char login[] ) const
		{
			if( strlen( login ) > MAX_LOGIN_LENGTH )
				return EndByLogin();
			sContestantData contestant_data;
			strcpy( contestant_data.Login, login );
			return LoginIterator( LoginIndex.find( cDataPointer< sContestantData >( &contestant_data ) ) );
		}
		LoginIterator EndByLogin() const
		{
			return LoginIterator( LoginIndex.end() );
		}
		bool GetByLogin( const char login[], sContestantData &contestant_data )
		{
			cLocker locker( *this );
			LoginIterator iterator = FindByLogin( login );
			if( iterator == EndByLogin() )
				return false;
			contestant_data = *iterator;
			return true;
		}
	};

	class cContestantDataBase : public cPointerObject< cWardContestantDataBase >
	{
	public:
		bool Create( /*bool auto_update = false*/ )
		{
			cWardContestantDataBase *ward_contestant_data_base;
			try
			{
				ward_contestant_data_base = new cWardContestantDataBase( /*auto_update*/ );
			}
			catch( ... )
			{
				return false;
			}
			if( !SwitchToWardObject( ward_contestant_data_base ) )
			{
				delete ward_contestant_data_base;
				return false;
			}
			return true;
		}
		void Destroy()
		{
			SwitchToSubObject( NULL );
		}
	};
}
